#include "tcpsocket.h"
#include <QDebug>

TcpSocket::TcpSocket(QObject *parent)
{
#ifdef Q_OS_WIN
    WSADATA wsaData;
    if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
        qDebug() << "Failed to initialize Winsock.";
        return ;
    }
#endif
}

TcpSocket::TcpSocket(QByteArray ip, unsigned short port, QObject *parent) : TcpSocket(parent)
{
    connectToHost(ip, port);
}

TcpSocket::~TcpSocket()
{
#ifdef Q_OS_WIN
    WSACleanup();
#endif
}

// 连接服务器
bool TcpSocket::connectToHost(QByteArray ip, unsigned short port)
{
    assert(port > 0 && port <= 65535);
    m_socket = socket(AF_INET, SOCK_STREAM, 0);
    assert(m_socket);

    qDebug() << "ip = " << ip << " port = " << port;

    struct sockaddr_in addr;
    memset(&addr, 0, sizeof(addr));
    addr.sin_family = AF_INET;
    addr.sin_port = htons(port);
    addr.sin_addr.s_addr = inet_addr(ip.data());

    int ret = ::connect(m_socket, reinterpret_cast<struct sockaddr*>(&addr), sizeof(addr));
    // int saved_errno = errno;

    if (ret == -1) {
        qDebug() << "Failed to connect to server";
    } else {
        qDebug() << "connect success......";
    }
    bool flag = (ret == 0 ? true : false);


    return flag;
}

// 接收数据
QByteArray TcpSocket::recvMsg(int timeout)
{
    bool flag = readTimeout(timeout);
    QByteArray array;
    if (flag) {
        // 接收到的数据 = 数据头 + 数据块
        int headLen = 0;
        int ret = readn(reinterpret_cast<char*>(&headLen), sizeof(int));
        if (ret != sizeof(int)) {
            // 未能正确读取到数据头，则返回空数组
            return QByteArray();
        }
        headLen = ntohl(headLen);
        qDebug() << "TcpSocket::recvMsg::headLen the data block : " << headLen;

        // 重新调整array大小以容纳即将读取的数据
        array.resize(headLen);

        // 读取消息内容
        ret = readn(array.data(), headLen);
        if (ret != headLen) {
            // 数据读写异常，清空数组
            array.clear();
        }

        /*
        // 申请一块内存
        char* data = new char[headLen];
        assert(data);

        ret = readn(data, headLen);
        if (ret == headLen) {
            // 数据正常
            array = QByteArray(data, headLen);
        } else {
            // 数据异常
            array = QByteArray();
        }
        delete[] data;
        */
    }

    return array;
}

// 发送数据
bool TcpSocket::sendMsg(QByteArray msg, int timeout)
{
    bool flag = writeTimeout(timeout);
    if (flag) {
        // 发送数据 = 数据头 + 数据块
        int headLen = htonl(msg.size());

        QByteArray data;
        data.prepend(reinterpret_cast<char*>(&headLen), sizeof(headLen));
        data.append(msg);

        // 发送数据
        int length = headLen + sizeof(int);
        int ret = writen(data, data.size());
        flag = (ret == length ? true : false);

        /*
         申请一块新的内存
        int length = headLen + sizeof(int);
        char* data = new int[length];
        assert(data);
        memcpy(data, &headLen, sizeof(int));
        memcpy(data + sizeof(int), msg.data(), msg.size());

        int ret = writen(data, length);
        flag = (ret == length ? true : false);

        delete[] data;
        */
    }

    return flag;
}

// 断开连接
void TcpSocket::disConnect()
{
    if (m_socket) {
#ifdef Q_OS_WIN
        closesocket(m_socket);
#endif

#ifdef Q_OS_LINUX
        close(m_socket);
#endif
    }
}

// 读数据
int TcpSocket::readn(char *buf, int count)
{
    int last = count;   // 剩余的字节数
    int size = 0;       // 每次读出的字节数
    char* pt = buf;
    while (last > 0) {
        if ((size = recv(m_socket, pt, last, 0)) < 0) {
            perror("recv");
            return -1;
        } else if (size == 0) {
            break;
        }
        last -= size;
        pt += size;
    }

    return count - last;
}

// 写数据
int TcpSocket::writen(const char *buf, int count)
{
    int last = count; // 剩余的字节数
    int size = 0;     // 写入的字节数
    const char* pt = buf;
    while (last > 0) {
        if ((size = send(m_socket, pt, last, 0)) < 0) {
            perror("send");
            return -1;
        } else if (size == 0) {
            continue;
        }
        last -= size;
        pt += size;
    }
    return count - last;
}

// 读缓冲区超时检测
bool TcpSocket::readTimeout(int timeout)
{
    if (timeout == -1) {
        return true; // 阻塞读
    }

    // 检测读缓冲区
#ifdef Q_OS_WIN
    int nfds = 0;
#endif

#ifdef Q_OS_LINUX
    int nfds = m_socket + 1;
#endif
    fd_set rdset;
    FD_ZERO(&rdset);
    FD_SET(m_socket, &rdset);
    struct timeval tmout;
    tmout.tv_sec = timeout;
    tmout.tv_usec = 0;
    int ret = select(nfds, &rdset, NULL, NULL, &tmout);
    bool flag = (ret > 0 ? true : false);

    return flag;
}

// 写缓冲区超时检测
bool TcpSocket::writeTimeout(int timeout)
{
    if (timeout == -1) {
        return true; // 阻塞写
    }

    // 检测读写缓冲区
#ifdef Q_OS_WIN
    int nfds = 0;
#endif

#ifdef Q_OS_LINUX
    int nfds = m_socket + 1;
#endif
    fd_set wset;
    FD_ZERO(&wset);
    FD_SET(m_socket, &wset);
    struct timeval tmout;
    tmout.tv_sec = timeout;
    tmout.tv_usec = 0;
    int ret = select(nfds, NULL, &wset, NULL, &tmout);
    bool flag = (ret > 0 ? true : false);

    return flag;
}

